﻿/*
 * Author: .net follower (http://dotnetfollower.com)
 * Url: http://bdcrequestlib.codeplex.com/
 * Title: SharePoint Bdc Requests
 * Copyright: � 2012 .net follower 
 * Usage: software is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint.BusinessData.SharedService;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;

namespace SharePointBdcRequests
{
    /// <summary>
    /// Abstracts an interaction with a BDC Metadata Store
    /// </summary>
    public class BdcMetaRequest : IDisposable
    {
        #region fields & properties
        protected readonly BdcRequestParams _bdcRequestParams;
        protected SPServiceContextScope     _spServiceContextScope;
        protected bool                      _isDisposed;

        protected readonly DataReceiver<IMetadataCatalog>   _metadataCatalog      = new DataReceiver<IMetadataCatalog>();
        protected readonly DataReceiver<ILobSystemInstance> _lobSystemInstance    = new DataReceiver<ILobSystemInstance>();
        protected readonly DataReceiver<IEntity>            _entity               = new DataReceiver<IEntity>();
        protected readonly DataReceiver<string[]>           _identifierFieldNames = new DataReceiver<string[]>();

        /// <summary>
        /// Name of the requested/found External System
        /// </summary>
        public string RequestedLobSystemInstanceName
        {
            get { return _bdcRequestParams.LobSystemInstanceName; }
        }

        /// <summary>
        /// Namespace of the requested/found External Content Type
        /// </summary>
        public string RequestedEntityNamespace
        {
            get { return _bdcRequestParams.EntityNamespace; }
        }

        /// <summary>
        /// Name of the requested/found External Content Type
        /// </summary>
        public string RequestedEntityName
        {
            get { return _bdcRequestParams.EntityName; }
        }

        /// <summary>
        /// BDC Metadata Store
        /// </summary>
        public virtual IMetadataCatalog FoundMetadataCatalog
        {
            get { return !_metadataCatalog.Received ? (_metadataCatalog.Data = GetMetadataCatalog()) : _metadataCatalog.Data; }
        }

        /// <summary>
        /// The found External Content Type
        /// </summary>
        public virtual IEntity FoundEntity
        {
            get { return !_entity.Received ? (_entity.Data = GetEntity()) : _entity.Data; }
        }

        /// <summary>
        /// Fields that make up an identifier of the found External Content Type. 
        /// More than one if the identifier is compound.
        /// </summary>
        public virtual string[] FoundEntityIdFieldNames
        {
            get { return !_identifierFieldNames.Received ? (_identifierFieldNames.Data = GetIdentifierFieldNames()) : _identifierFieldNames.Data; }
        }

        /// <summary>
        /// The found External System Instance
        /// </summary>
        public virtual ILobSystemInstance FoundLobSystemInstance
        {
            get { return !_lobSystemInstance.Received ? (_lobSystemInstance.Data = GetLobSystemInstance()) : _lobSystemInstance.Data; }
        }

        /// <summary>
        /// Fields which make up a default view of the found External Content Type. Overridden in derived classes.
        /// </summary>
        public virtual List<IField> FoundViewFields
        {
            get { return FoundEntity.GetDefaultSpecificFinderView().Fields.ToList(); }
        }
        #endregion

        #region public methods
        /// <summary>
        /// Initializes a new instance of the BDCMetaRequest class
        /// </summary>
        /// <param name="bdcRequestParams">Input parameters</param>
        public BdcMetaRequest(BdcRequestParams bdcRequestParams)
        {
            EnsureParamIsNotNull("bdcRequestParams", bdcRequestParams);
            _bdcRequestParams = bdcRequestParams;

            if (_bdcRequestParams.SpSite == null)
                _bdcRequestParams.SpSite = SPContext.Current.Site;
            EnsureParamIsNotNull("SPSite", _bdcRequestParams.SpSite);

            // check if lobSysInstanceName isn't NULL or empty string
            EnsureParamIsNotNullOrEmpty("LobSysInstanceName", _bdcRequestParams.LobSystemInstanceName);
            // check if bdcEntityName isn't NULL or empty string
            EnsureParamIsNotNullOrEmpty("EntityName", _bdcRequestParams.EntityName);

            if (string.IsNullOrEmpty(_bdcRequestParams.EntityNamespace))
                _bdcRequestParams.EntityNamespace = _bdcRequestParams.LobSystemInstanceName;
        }

        /// <summary>
        /// Releases resources
        /// </summary>
        public virtual void Dispose()
        {
            if (_metadataCatalog.Data != null)
            {
                IDisposable disposable = _metadataCatalog.Data as IDisposable;
                if(disposable != null)
                    disposable.Dispose();
            }

            _metadataCatalog.Reset();
            _entity.Reset();
            _lobSystemInstance.Reset();
            _identifierFieldNames.Reset();

            if (_spServiceContextScope != null)
            {
                _spServiceContextScope.Dispose();
                _spServiceContextScope = null;
            }
            _isDisposed = true;
        }
        #endregion

        #region internal methods
        /// <summary>
        /// Returns BDC Metadata Store
        /// </summary>
        protected IMetadataCatalog GetMetadataCatalog()
        {
            VerifyDisposition();
            EnsureServiceContextScopeCreated();
            BdcService service = SPFarm.Local.Services.GetValue<BdcService>(String.Empty);
            return service.GetDatabaseBackedMetadataCatalog(SPServiceContext.Current);
        }
        /// <summary>
        /// Returns Entity
        /// </summary>
        protected IEntity GetEntity()
        {
            VerifyDisposition();
            return FoundMetadataCatalog.GetEntity(_bdcRequestParams.EntityNamespace, _bdcRequestParams.EntityName);
        }
        /// <summary>
        /// Returns LobSystemInstance
        /// </summary>
        protected ILobSystemInstance GetLobSystemInstance()
        {
            VerifyDisposition();
            return FoundMetadataCatalog.GetLobSystem(_bdcRequestParams.LobSystemInstanceName).GetLobSystemInstances()[_bdcRequestParams.LobSystemInstanceName];
        }
        /// <summary>
        /// Returns fields making up an ID of the found Entity
        /// </summary>
        protected string[] GetIdentifierFieldNames()
        {
            VerifyDisposition();
            return FoundEntity.GetIdentifiers().Select(i => i.Name).ToArray();
        }
        /// <summary>
        /// Creates a SPServiceContextScope object if it hasn't been created yet
        /// </summary>
        protected void EnsureServiceContextScopeCreated()
        {
            if (_spServiceContextScope == null)
                _spServiceContextScope = new SPServiceContextScope(SPServiceContext.GetContext(_bdcRequestParams.SpSite));
        }
        /// <summary>
        /// Checks whether the current object has already been disposed
        /// </summary>
        protected virtual void VerifyDisposition()
        {
            if (_isDisposed)
                throw new InvalidOperationException("Object is already disposed!");
        }

        /// <summary>
        /// Throws an exception if the passed parameter value is NULL or empty string
        /// </summary>
        /// <param name="paramName">Parameter name</param>
        /// <param name="value">Parameter value</param>
        protected static void EnsureParamIsNotNullOrEmpty(string paramName, string value)
        {
            if (string.IsNullOrEmpty(value))
                throw new ArgumentException(string.Format("{0} must not be NULL or empty!", paramName), paramName);
        }

        /// <summary>
        /// Throws an exception if the passed parameter value is NULL
        /// </summary>
        /// <param name="paramName">Parameter name</param>
        /// <param name="value">Parameter value</param>
        protected static void EnsureParamIsNotNull(string paramName, object value)
        {
            if (value == null)
                throw new ArgumentNullException(paramName, string.Format("{0} must not be NULL!", paramName));
        }
        #endregion
    }
}
